package server

import (
	"context"
	"log"
	"net"
	"net/http"
	"time"

	"github.com/gorilla/mux"
	"github.com/pkg/errors"
)

type Server interface {
	Start() error
	Stop() error
}

type ServerOption func(*HttpServer)

type HttpServer struct {
	*http.Server
	lis     net.Listener
	timeout time.Duration
	router  *mux.Router
	network string
	address string
}

func Listen(address string) ServerOption {
	return func(s *HttpServer) {
		s.address = address
	}
}

func NewHttpServer(ops ...ServerOption) *HttpServer {
	svc := &HttpServer{
		network: "tcp",
		address: ":0",
		timeout: time.Second,
	}
	for _, o := range ops {
		o(svc)
	}
	svc.router = mux.NewRouter()
	svc.Server = &http.Server{Handler: svc}
	return svc
}

func (s *HttpServer) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	ctx, cancel := context.WithTimeout(req.Context(), s.timeout)
	defer cancel()
	s.router.ServeHTTP(res, req.WithContext(ctx))
}

// HandleFunc registers a new route with a matcher for the URL path.
func (s *HttpServer) HandleFunc(path string, h http.HandlerFunc) {
	s.router.HandleFunc(path, h)
}

func (s *HttpServer) Start() error {
	lis, err := net.Listen(s.network, s.address)
	if err != nil {
		return err
	}
	s.lis = lis
	log.Printf("[HTTP] server listening on: %s", lis.Addr().String())
	if err := s.Serve(lis); !errors.Is(err, http.ErrServerClosed) {
		return err
	}
	return nil
}

func (s *HttpServer) Stop() error {
	log.Printf("[HTTP] server stopping")
	return s.Shutdown(context.Background())
}
